package com.gotobject.junit.internal;

import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Huascar A. Sanchez (huascar.sanchez@gmail.com)
 */
public class Bootstrapper {
	private final Object test;


	public Bootstrapper(Object test){
		this.test = test;
	}

	@SuppressWarnings({"UnusedCatchParameter"})
    public void boostrap(Class<? extends Module>... available){
		final List<Module> 				mods 	= new ArrayList<Module>();
		try {
			for(Class<? extends Module> each : available){
				mods.add(each.newInstance());
			}

			// temp used my injector
			final Injector inj = Guice.createInjector(mods);

			final Field[] declaredOnes = test.getClass().getDeclaredFields();
			for(Field each : declaredOnes){
				if(each.isAnnotationPresent(Inject.class)){
                    assistInjection(test, each, inj);
                }
			}
		} catch (Throwable e){
			throw new RuntimeException(e);			
		}
	}

    private static void assistInjection(Object tClass, Field each, Injector injector) throws Exception {
        // ugly, but what can I do?
        each.setAccessible(true);
        // named scenario
        if(each.isAnnotationPresent(Named.class)){
            final Named nElem = each.getAnnotation(Named.class);
            each.set(tClass, injector.getInstance(Key.get(each.getType(), nElem)));
            return;
        }

        final Provider<?> p = injector.getProvider(Key.get(each.getGenericType()));
        if(null != p){
            each.set(tClass, p.get());
            return;
        }

        // TypeLiteral & normal binding retrieval.
        final boolean isTradInjection = injector.getBinding(Key.get(each.getType())) != null;
        if(isTradInjection){
           each.set(tClass, injector.getInstance(each.getType()));
        } else {
            final Object value = injector.findBindingsByType(TypeLiteral.get(each.getGenericType())).get(0).getProvider().get();
            each.set(tClass, value);
        }
    }
}
